﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace TY.Utilities
{
    public class CRC16
    {
        #region CRC16相关功能部分
        /// <summary>
        /// 对于字节数组做标准CRC16校验并返回CRC16值
        /// </summary>
        /// <param name="pData">字节数组</param>
        /// <returns>Uint16类型CRC16值</returns>
        public static UInt16 ToCRC16(byte[] pData)
        {
            int i = 0;
            int j = 0;
            UInt32 crc = 0xffff;
            for (i = 0; i < pData.Length; i++)
            {
                crc = (crc / 256) * 256 + (crc % 256) ^ pData[i];
                for (j = 0; j <= 7; j++)
                {
                    UInt32 d = 0;
                    d = crc & 1;
                    crc = crc / 2;
                    if (d == 1)
                    {
                        crc = crc ^ 0xa001;
                    }
                }
            }
            return (UInt16)(crc & 0xffff);
        }
        /// <summary>
        /// 由源字节数组组装出包含CRC校验的字节数组
        /// </summary>
        /// <param name="pData">源字节数组</param>
        /// <param name="pLowFirst">CRC16值是否低地址在前存储</param>
        /// <returns>包含CRC校验的字节数组</returns>
        public static byte[] CRC16ArrayMake(byte[] pSrcData, bool pLowFirst = true)
        {
            UInt16 CRCValue = ToCRC16(pSrcData);
            byte CRCHgt = (byte)(CRCValue >> 8);
            byte CRCLow = (byte)(CRCValue & 0x00ff);
            byte[] NewByteArray = new byte[pSrcData.Length + 2];
            pSrcData.CopyTo(NewByteArray, 0);
            if (pLowFirst)
            {
                NewByteArray[NewByteArray.Length - 2] = CRCLow;
                NewByteArray[NewByteArray.Length - 1] = CRCHgt;
            }
            else
            {
                NewByteArray[NewByteArray.Length - 2] = CRCHgt;
                NewByteArray[NewByteArray.Length - 1] = CRCLow;
            }
            return NewByteArray;
        }

        /// <summary>
        /// 验证一个尾部包含CRC16校验信息的字节数组的完整有效性
        /// </summary>
        /// <param name="pCRCData">尾部包含CRC16校验信息的字节数组</param>
        /// <param name="pLowFirst">CRC16值是否低地址在前存储</param>
        /// <returns>数据完整有效性</returns>
        public static bool CRC16ArrayCheck(byte[] pCRCData, bool pLowFirst)
        {
            if (pCRCData.Length < 3)
            {
                return false;
            }
            else
            {
                byte[] SrcData = new byte[pCRCData.Length - 2];
                Array.Copy(pCRCData, SrcData, pCRCData.Length - 2);
                UInt16 CRCValue = 0;
                byte CRCBhd = pCRCData[pCRCData.Length - 2];
                byte CRCLas = pCRCData[pCRCData.Length - 1];
                if (pLowFirst)
                {
                    CRCValue = CRCBhd;
                    CRCValue |= (UInt16)(((UInt16)CRCLas) << 8);
                }
                else
                {
                    CRCValue = CRCLas;
                    CRCValue |= (UInt16)(((UInt16)CRCBhd) << 8);
                }
                return ToCRC16(SrcData) == CRCValue;
            }
        }

        /// <summary>
        /// 验证一个尾部包含CRC16校验信息的字节数组的完整有效性
        /// </summary>
        /// <param name="pCRCData">尾部包含CRC16校验信息的字节数组</param>
        /// <returns>数据完整有效性</returns>
        public static bool CRC16ArrayCheck(byte[] pCRCData)
        {
            return (CRC16ArrayCheck(pCRCData, true) || CRC16ArrayCheck(pCRCData, false));
        }

        /// <summary>
        /// 缓存分析
        /// </summary>
        /// <param name="pList">缓存</param>
        /// <returns>若捕获到正确的数据则返回真，否则返回假</returns>
        public static byte[] BufferMatch(List<byte> pList)
        {
            for (int i = 0; i < pList.Count - 2; ++i)
            {
                List<byte> Tmp = pList.Skip(i).Take(pList.Count - i).ToList();
                if (CRC16ArrayCheck(Tmp.ToArray()))
                {
                    pList.Clear();
                    return Tmp.ToArray();
                }
            }
            return null;
        }
        #endregion
    }
}
